package com.jzo2o.market.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.jzo2o.common.expcetions.BadRequestException;
import com.jzo2o.common.expcetions.CommonException;
import com.jzo2o.common.expcetions.DBException;
import com.jzo2o.common.model.PageResult;
import com.jzo2o.common.utils.*;
import com.jzo2o.market.constants.TabTypeConstants;
import com.jzo2o.market.enums.ActivityStatusEnum;
import com.jzo2o.market.handler.XxlJobHandler;
import com.jzo2o.market.enums.ActivityTypeEnum;
import com.jzo2o.market.enums.CouponStatusEnum;
import com.jzo2o.market.mapper.ActivityMapper;
import com.jzo2o.market.model.domain.Activity;
import com.jzo2o.market.model.domain.Coupon;
import com.jzo2o.market.model.dto.request.ActivityQueryForPageReqDTO;
import com.jzo2o.market.model.dto.request.ActivitySaveReqDTO;
import com.jzo2o.market.model.dto.response.ActivityInfoResDTO;
import com.jzo2o.market.model.dto.response.SeizeCouponInfoResDTO;
import com.jzo2o.market.service.IActivityService;
import com.jzo2o.market.service.ICouponService;
import com.jzo2o.market.service.ICouponUseBackService;
import com.jzo2o.market.service.ICouponWriteOffService;
import com.jzo2o.mysql.utils.PageUtils;
import com.xxl.job.core.context.XxlJobHelper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.HashOperations;
import com.jzo2o.mysql.utils.PageHelperUtils;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;


import static com.jzo2o.market.constants.RedisConstants.RedisKey.ACTIVITY_CACHE_LIST;
import static com.jzo2o.market.constants.RedisConstants.RedisKey.COUPON_RESOURCE_STOCK;
import static com.jzo2o.market.enums.ActivityStatusEnum.*;


/**
 * <p>
 * 服务实现类
 * </p>
 *
 * @author itcast
 * @since 2023-09-16
 */
@Slf4j
@Service
public class ActivityServiceImpl extends ServiceImpl<ActivityMapper, Activity> implements IActivityService {
    //它在整个应用程序中只有一个副本，所有类的实例共享这个值 用于生成排序字段时的计算
    private static final int MILLION = 1000000;

    @Resource
    private RedisTemplate redisTemplate;

    @Resource
    private ICouponService couponService;

    @Resource
    private ICouponWriteOffService couponWriteOffService;

    @Resource
    private IActivityService activityService;

    @Resource
    private ActivityServiceImpl activityServiceImpl;
    @Resource
    private ICouponUseBackService couponUseBackService;


    @Override
    public void add(ActivitySaveReqDTO dto) {

        //判断是否传了id, 如果传了id就是修改, 没传就是新增
        Long id = dto.getId();
        if (id != null) {
            //修改
            Activity activity = getById(id);
            if (activity == null || activity.getIsDeleted() != 0) {
                throw new BadRequestException("没有id" + id + "的优惠券");
            }
            if (activity.getStatus() != NO_DISTRIBUTE.getStatus()) {
                throw new BadRequestException("只允许修改待生效的活动");
            }
            //1.1发放时间开始时间不能小于当前时间
            if (dto.getDistributeStartTime().isBefore(LocalDateTime.now())) {
                throw new BadRequestException("发放时间开始时间不能早于当前时间");
            }
            //1.2发放结束时间不能早于发放开始时间
            if (dto.getDistributeEndTime().isBefore(dto.getDistributeStartTime())) {
                throw new BadRequestException("发放结束时间不能早于发放开始时间");
            }
            //2.判断是优惠券还是满减类型
            if (dto.getType() == ActivityTypeEnum.AMOUNT_DISCOUNT.getType()) {
                //如果是满减 discount_amount必须有值 且>0
                if (dto.getDiscountAmount().compareTo(BigDecimal.ZERO) != 1) {
                    throw new BadRequestException("满减金额必须大于0");
                }
                //构建参数插入数据库
                activityService.modifyById(dto);
            } else {
                //如果是优惠券 discount_rate必须有值 >0 且 <100 的整数
                if (dto.getDiscountRate() <= 0 && dto.getDiscountRate() >= 100) {
                    throw new BadRequestException("折扣输入值必须在0到100");
                }
                activityService.modifyById(dto);
            }

        }else {
            //1.1发放时间开始时间不能小于当前时间
            if (dto.getDistributeStartTime().isBefore(LocalDateTime.now())) {
                throw new BadRequestException("发放时间开始时间不能早于当前时间");
            }
            //1.2发放结束时间不能早于发放开始时间
            if (dto.getDistributeEndTime().isBefore(dto.getDistributeStartTime())) {
                throw new BadRequestException("发放结束时间不能早于发放开始时间");
            }
            //2.判断是优惠券还是满减类型
            if (dto.getType() == ActivityTypeEnum.AMOUNT_DISCOUNT.getType()) {
                //如果是满减 discount_amount必须有值 且>0
                if (dto.getDiscountAmount().compareTo(BigDecimal.ZERO) != 1) {
                    throw new BadRequestException("满减金额必须大于0");
                }
                //构建参数插入数据库
                activityService.addActivity(dto);
            } else {
                //如果是优惠券 discount_rate必须有值 >0 且 <100 的整数
                if (dto.getDiscountRate() <= 0 && dto.getDiscountRate() >= 100) {
                    throw new BadRequestException("折扣输入值必须在0到100");
                }
                activityService.addActivity(dto);
            }
        }



    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void addActivity(ActivitySaveReqDTO dto) {
        //构建自增id
        Long id = redisTemplate.opsForValue().increment("Redis::activityId", 1);
        Activity activity = BeanUtils.toBean(dto, Activity.class);
        activity.setId(id);
        activity.setStatus(Integer.valueOf(NO_DISTRIBUTE.getStatus()));
        activity.setStockNum(dto.getTotalNum());
        boolean saved = save(activity);
        if (!saved) {
            throw new DBException("新增优惠券失败");
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void modifyById(ActivitySaveReqDTO dto) {
        boolean updated = lambdaUpdate()
                .eq(Activity::getId, dto.getId())
                .set(Activity::getName, dto.getName())
                .set(Activity::getType, dto.getType())
                .set(Activity::getAmountCondition, dto.getAmountCondition())
                .set(ObjectUtils.isNotEmpty(dto.getDiscountRate()),Activity::getDiscountRate, dto.getDiscountRate())
                .set(ObjectUtils.isNotEmpty(dto.getDiscountAmount()),Activity::getDiscountAmount, dto.getDiscountAmount())
                .set(Activity::getValidityDays, dto.getValidityDays())
                .set(Activity::getDistributeStartTime, dto.getDistributeStartTime())
                .set(Activity::getDistributeEndTime, dto.getDistributeEndTime())
                .set(Activity::getTotalNum, dto.getTotalNum())
                .set(Activity::getStockNum,dto.getTotalNum())
                .update();
        if (!updated) {
            throw new DBException("修改活动失败");
        }
    }

    @Override
    public PageResult<ActivityInfoResDTO> selectByPage(ActivityQueryForPageReqDTO dto) {
        return PageHelperUtils.selectPage(dto,()->baseMapper.queryActivityByCondition(dto));
    }

    @Override
    public void updateState() {
        activityServiceImpl.takeEffect();
        activityServiceImpl.loseEfficacy();
    }
    //生效
    @Transactional(rollbackFor = Exception.class)
    public void takeEffect(){
        List<Activity> activities = lambdaQuery()
                .eq(Activity::getStatus, NO_DISTRIBUTE.getStatus())
                .le(Activity::getDistributeStartTime, LocalDateTime.now())
                .ge(Activity::getDistributeEndTime,LocalDateTime.now())
                .list();
        log.info("activities" + null);
        if (CollUtils.isEmpty(activities)) {
            XxlJobHelper.log("没有在活动期间的优惠卷");
            return;
        }
        for (Activity activity : activities) {
            lambdaUpdate()
                    .eq(Activity::getId, activity.getId())
                    .set(Activity::getStatus, DISTRIBUTING.getStatus())
                    .update();
        }
    }

    //失效
    @Transactional(rollbackFor = Exception.class)
    public void loseEfficacy(){
        List<Activity> activities = lambdaQuery()
                .in(Activity::getStatus,
                        DISTRIBUTING.getStatus(),
                        NO_DISTRIBUTE.getStatus())
                .lt(Activity::getDistributeEndTime, LocalDateTime.now())
                .isNotNull(Activity::getDistributeEndTime)
                .list();
        if (CollUtils.isEmpty(activities)) {
            XxlJobHelper.log("没有过期的优惠卷");
            return;
        }
        for (Activity activity : activities) {
            lambdaUpdate()
                    .eq(Activity::getId, activity.getId())
                    .set(Activity::getStatus, LOSE_EFFICACY.getStatus())
                    .update();
        }
    }
    @Override
    public ActivityInfoResDTO selectById(Long id) {
        Activity activity = getById(id);
        return  BeanUtils.toBean(activity,ActivityInfoResDTO.class);
    }


    @Override
    @Transactional(rollbackFor = Exception.class)
    public void revokeByActivityId(Long id) {
        Activity activity = getById(id);
        if (ObjectUtils.isNotEmpty(activity)
                && ((ObjectUtils.equal(activity.getStatus(), NO_DISTRIBUTE.getStatus()))
                || (ObjectUtils.equal(activity.getStatus(), DISTRIBUTING.getStatus())))) {
            boolean update = lambdaUpdate().eq(Activity::getId, id)
                    .set(Activity::getStatus, ActivityStatusEnum.VOIDED.getStatus())
                    .update();
            if (!update){
                throw new CommonException("撤销失败");
            }
            List<Coupon> couponList = couponService.lambdaQuery().eq(Coupon::getActivityId, id).list();
            for (Coupon coupon : couponList) {
                boolean updated = couponService.lambdaUpdate().eq(Coupon::getId, coupon.getId())
                        .eq(Coupon::getStatus, CouponStatusEnum.NO_USE.getStatus())
                        .set(Coupon::getIsDeleted, 1)
                        .update();

            }

        }else{
            throw new CommonException("撤销失败");
        }
    }



    @Override
    public ActivityInfoResDTO getActivityInfoByIdFromCache(Long id) {
        // 1.从缓存中获取活动信息
        Object activityList = redisTemplate.opsForValue().get(ACTIVITY_CACHE_LIST);
        if (ObjectUtils.isNull(activityList)) {
            return null;
        }
        // 2.过滤指定活动信息
        List<ActivityInfoResDTO> list = JsonUtils.toList(activityList.toString(), ActivityInfoResDTO.class);
        if (CollUtils.isEmpty(list)) {
            return null;
        }
        // 3.过滤指定活动
        return list.stream()
                .filter(activityInfoResDTO -> activityInfoResDTO.getId().equals(id))
                .findFirst().orElse(null);
    }

    @Override
    public List<SeizeCouponInfoResDTO> queryForListFromCache(Integer tabType) {
        // 查询Redis的活动列表ACTIVITY:LIST
        Object seizeCouponInfoStr = redisTemplate.opsForValue().get(ACTIVITY_CACHE_LIST);
        if (ObjectUtils.isNull(seizeCouponInfoStr)) {
            return CollUtils.emptyList();
        }
        //将json转为List
        List<SeizeCouponInfoResDTO> seizeCouponInfoResDTOS = JsonUtils.toList(seizeCouponInfoStr.toString(), SeizeCouponInfoResDTO.class);
        // 根据tabType过滤活动列表（tabType和表中状态值不一致，下面进行统一）
        int queryStatus = tabType == TabTypeConstants.SEIZING ? DISTRIBUTING.getStatus() : NO_DISTRIBUTE.getStatus();

        // 查询活动状态及时返回前端(设置剩余数量，实际状态)
        // stream流的peek是中间过程方法
        List<SeizeCouponInfoResDTO> collect = seizeCouponInfoResDTOS.stream()
                .filter(v -> queryStatus == getStatus(v.getDistributeStartTime(), v.getDistributeEndTime(), v.getStatus()))
                .peek(v -> {
                    v.setRemainNum(v.getStockNum());
                    v.setStatus(queryStatus);
                }).collect(Collectors.toList());
        return collect;
    }

    private int getStatus(LocalDateTime distributeStartTime, LocalDateTime distributeEndTime, Integer status) {
        if (NO_DISTRIBUTE.equals(status) &&
                distributeStartTime.isBefore(DateUtils.now()) &&
                distributeEndTime.isAfter(DateUtils.now())) {
            // 如果当前状态待生效 如果开始时间小于当前时间并且结束时间大于当前时间 活动正在进行
            return DISTRIBUTING.getStatus();
        } else if (NO_DISTRIBUTE.equals(status) &&
                distributeEndTime.isBefore(DateUtils.now())) {
            // 当前状态待生效 结束时间小于当前时间 活动已结束
            return LOSE_EFFICACY.getStatus();
        } else if (DISTRIBUTING.equals(status) &&
                distributeEndTime.isBefore(DateUtils.now())) {
            // 当前状态为进行中 结束时间小于当前时间 活动变为已结束
            return LOSE_EFFICACY.getStatus();
        }
        // 上述都不成立则原状态返回
        return status;

    }

    @Override
    public void preHeat() {
        //查询进行中还未到结束的优惠券活动， 1个月内待开始的优惠券活动

        List<Activity> list = lambdaQuery()
                .le(Activity::getDistributeStartTime, LocalDateTime.now().plusDays(30))
                .in(Activity::getStatus, Arrays.asList(1, 2))
                .orderByAsc(Activity::getDistributeStartTime)
                .list();
        if (CollUtils.isEmpty(list)) {
            //防止缓存穿透
            list = new ArrayList<>();
        }
        // 查到的活动信息存入Redis
        // 2.数据转换
        List<SeizeCouponInfoResDTO> seizeCouponInfoResDTOS = BeanUtils.copyToList(list, SeizeCouponInfoResDTO.class);
        String seizeCouponInfoStr = JsonUtils.toJsonStr(seizeCouponInfoResDTOS);
        redisTemplate.opsForValue().set(ACTIVITY_CACHE_LIST, seizeCouponInfoStr);

        // 对未开始的活动直接更新库存即可
        list.stream()
                .filter(v -> NO_DISTRIBUTE.getStatus() == getStatus(v.getDistributeStartTime(), v.getDistributeEndTime(), v.getStatus()))
                .forEach(v -> {
                    redisTemplate.opsForHash().put(String.format(COUPON_RESOURCE_STOCK, v.getId() % 10), v.getId(), v.getTotalNum());
                });
        // 对已经开始的活动也要额外操作 如果活动已经开始但是定时任务还没进行缓存预热
        // 所以如果redis未设置库存才进行设置 如果已经存在则不进行任何操作
        list.stream()
                .filter(v -> DISTRIBUTING.getStatus() == getStatus(v.getDistributeStartTime(), v.getDistributeEndTime(), v.getStatus()))
                .forEach(v -> {
                    redisTemplate.opsForHash().putIfAbsent(String.format(COUPON_RESOURCE_STOCK, v.getId() % 10), v.getId(), v.getTotalNum());
                });
    }

    /*@Override
    public PageResult<ActivityInfoResDTO> queryForPage(ActivityQueryForPageReqDTO activityQueryForPageReqDTO) {
        LocalDateTime now = DateUtils.now();
        // 1.查询准备
        LambdaQueryWrapper<Activity> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        // 查询条件
        lambdaQueryWrapper.eq(ObjectUtils.isNotNull(activityQueryForPageReqDTO.getId()), Activity::getId, activityQueryForPageReqDTO.getId())
                .like(StringUtils.isNotEmpty(activityQueryForPageReqDTO.getName()), Activity::getName, activityQueryForPageReqDTO.getName())
                .eq(ObjectUtils.isNotNull(activityQueryForPageReqDTO.getType()), Activity::getType, activityQueryForPageReqDTO.getType())
                .eq(ObjectUtils.isNotNull(activityQueryForPageReqDTO.getStatus()), Activity::getStatus, activityQueryForPageReqDTO.getStatus());

        // 排序
        lambdaQueryWrapper.orderByDesc(Activity::getId);
        // 分页
        Page<Activity> activityPage = new Page<>(activityQueryForPageReqDTO.getPageNo().intValue(), activityQueryForPageReqDTO.getPageSize().intValue());
        activityPage = baseMapper.selectPage(activityPage, lambdaQueryWrapper);
        return PageUtils.toPage(activityPage, ActivityInfoResDTO.class);
    }*/

    @Override
    public ActivityInfoResDTO queryById(Long id) {
        // 1.获取活动
        Activity activity = baseMapper.selectById(id);

        // 判空
        if (activity == null) {
            return new ActivityInfoResDTO();
        }
        // 2.数据转换，并返回信息
        ActivityInfoResDTO activityInfoResDTO = BeanUtils.toBean(activity, ActivityInfoResDTO.class);
        // 设置状态
        activityInfoResDTO.setStatus(getStatus(activity.getDistributeStartTime(), activity.getDistributeEndTime(), activity.getStatus()));
         //3.领取数量
        Integer receiveNum = couponService.countReceiveNumByActivityId(activity.getId()) ;
        activityInfoResDTO.setReceiveNum(receiveNum);

        //Integer receiveNumm = couponService.countReceiveNumByActivityId(activity.getId());

        //Integer receiveNum =  activity.getTotalNum() ;
        //Integer receiveNum =  2;

        // 4.核销量
        Integer writeOffNum = couponService.lambdaQuery().eq(Coupon::getActivityId,id)
                .eq(Coupon::getStatus,CouponStatusEnum.USED.getStatus())
                .count();
        activityInfoResDTO.setWriteOffNum(NumberUtils.null2Zero(writeOffNum));

        //
        return activityInfoResDTO;
    }



    /*@Override
    public void save(ActivitySaveReqDTO activitySaveReqDTO) {
        // 1.逻辑校验
        // 1.逻辑校验
        activitySaveReqDTO.check();
        // 2.活动数据组装
        // 转换
        Activity activity = BeanUtils.toBean(activitySaveReqDTO, Activity.class);
        // 状态
        activity.setStatus(NO_DISTRIBUTE.getStatus());
        //库存
        activity.setTotalNum(activitySaveReqDTO.getTotalNum());
        if (activitySaveReqDTO.getId() == null) {
            activity.setId(IdUtils.getSnowflakeNextId());
        }
        //排序字段
//        long sortBy = DateUtils.toEpochMilli(activity.getDistributeStartTime()) * MILLION + activity.getId() % MILLION;
        // 3.保存
        saveOrUpdate(activity);

    }*/

//    @Override
//    public void revoke(Long id) {
//        // 1.活动作废
//        boolean update = lambdaUpdate()
//                .set(Activity::getStatus, ActivityStatusEnum.VOIDED.getStatus())
//                .eq(Activity::getId, id)
//                .in(Activity::getStatus, Arrays.asList(NO_DISTRIBUTE.getStatus(), DISTRIBUTING.getStatus()))
//                .update();
//        if (!update) {
//            return;
//        }
//        // 2.未使用优惠券作废
//        couponService.revoke(id);
//
//    }
    }

